જાવાસ્ક્રિપ્ટ અસિંક ઇટરેટર હેલ્પર 'સ્કેન'ની ઊંડાણપૂર્વક સમજ, તેની કાર્યક્ષમતા, ઉપયોગના કેસો અને અસિંક્રનસ સંચિત પ્રક્રિયા માટેના ફાયદાઓનું અન્વેષણ.
જાવાસ્ક્રિપ્ટ અસિંક ઇટરેટર હેલ્પર: સ્કેન - અસિંક સંચિત પ્રક્રિયા
આધુનિક જાવાસ્ક્રિપ્ટ ડેવલપમેન્ટમાં અસિંક્રનસ પ્રોગ્રામિંગ એક પાયાનો પથ્થર છે, ખાસ કરીને જ્યારે I/O-બાઉન્ડ ઓપરેશન્સ, જેમ કે નેટવર્ક વિનંતીઓ અથવા ફાઇલ સિસ્ટમ ક્રિયાપ્રતિક્રિયાઓ સાથે કામ કરતી વખતે. અસિંક ઇટરેટર્સ, જે ES2018 માં રજૂ કરવામાં આવ્યા હતા, તે અસિંક્રનસ ડેટાના સ્ટ્રીમ્સને હેન્ડલ કરવા માટે એક શક્તિશાળી મિકેનિઝમ પ્રદાન કરે છે. `scan` હેલ્પર, જે RxJS જેવી લાઇબ્રેરીઓમાં વારંવાર જોવા મળે છે અને હવે એકલ યુટિલિટી તરીકે વધુને વધુ ઉપલબ્ધ છે, તે આ અસિંક્રનસ ડેટા સ્ટ્રીમ્સની પ્રક્રિયા માટે વધુ સંભાવનાઓ ખોલે છે.
અસિંક ઇટરેટર્સને સમજવું
`scan` માં ઊંડા ઉતરતા પહેલાં, ચાલો યાદ કરીએ કે અસિંક ઇટરેટર્સ શું છે. અસિંક ઇટરેટર એક ઑબ્જેક્ટ છે જે અસિંક ઇટરેટર પ્રોટોકોલનું પાલન કરે છે. આ પ્રોટોકોલ `next()` મેથડને વ્યાખ્યાયિત કરે છે જે એક પ્રોમિસ પરત કરે છે જે બે પ્રોપર્ટીઝવાળા ઑબ્જેક્ટમાં રિઝોલ્વ થાય છે: `value` (ક્રમમાં આગલું મૂલ્ય) અને `done` (એક બુલિયન જે સૂચવે છે કે ઇટરેટર સમાપ્ત થયું છે કે નહીં). અસિંક ઇટરેટર્સ ખાસ કરીને એવા ડેટા સાથે કામ કરતી વખતે ઉપયોગી છે જે સમય જતાં આવે છે, અથવા એવો ડેટા જેને મેળવવા માટે અસિંક્રનસ ઓપરેશન્સની જરૂર પડે છે.
અહીં અસિંક ઇટરેટરનું એક મૂળભૂત ઉદાહરણ છે:
async function* generateNumbers() {
yield 1;
yield 2;
yield 3;
}
async function main() {
const iterator = generateNumbers();
let result = await iterator.next();
console.log(result); // { value: 1, done: false }
result = await iterator.next();
console.log(result); // { value: 2, done: false }
result = await iterator.next();
console.log(result); // { value: 3, done: false }
result = await iterator.next();
console.log(result); // { value: undefined, done: true }
}
main();
`scan` હેલ્પરનો પરિચય
`scan` હેલ્પર (જે `accumulate` અથવા `reduce` તરીકે પણ ઓળખાય છે) એક અસિંક ઇટરેટરને દરેક મૂલ્ય પર એક્યુમ્યુલેટર ફંક્શન લાગુ કરીને અને સંચિત પરિણામ ઉત્સર્જિત કરીને રૂપાંતરિત કરે છે. આ એરે પરની `reduce` મેથડ જેવું જ છે, પરંતુ તે અસિંક્રનસ રીતે અને ઇટરેટર્સ પર કાર્ય કરે છે.
સારમાં, `scan` એક અસિંક ઇટરેટર, એક એક્યુમ્યુલેટર ફંક્શન, અને વૈકલ્પિક પ્રારંભિક મૂલ્ય લે છે. સોર્સ ઇટરેટર દ્વારા ઉત્સર્જિત દરેક મૂલ્ય માટે, એક્યુમ્યુલેટર ફંક્શનને પાછલા સંચિત મૂલ્ય (અથવા જો તે પ્રથમ પુનરાવર્તન હોય તો પ્રારંભિક મૂલ્ય) અને ઇટરેટરમાંથી વર્તમાન મૂલ્ય સાથે કૉલ કરવામાં આવે છે. એક્યુમ્યુલેટર ફંક્શનનું પરિણામ આગલું સંચિત મૂલ્ય બને છે, જે પછી પરિણામી અસિંક ઇટરેટર દ્વારા ઉત્સર્જિત થાય છે.
સિંટેક્સ અને પેરામીટર્સ
`scan` નો ઉપયોગ કરવા માટેનું સામાન્ય સિંટેક્સ નીચે મુજબ છે:
async function* scan(sourceIterator, accumulator, initialValue) {
let accumulatedValue = initialValue;
for await (const value of sourceIterator) {
accumulatedValue = accumulator(accumulatedValue, value);
yield accumulatedValue;
}
}
- `sourceIterator`: રૂપાંતરિત કરવા માટેનો અસિંક ઇટરેટર.
- `accumulator`: એક ફંક્શન જે બે દલીલો લે છે: પાછલું સંચિત મૂલ્ય અને ઇટરેટરમાંથી વર્તમાન મૂલ્ય. તેણે નવું સંચિત મૂલ્ય પરત કરવું જોઈએ.
- `initialValue` (વૈકલ્પિક): એક્યુમ્યુલેટર માટેનું પ્રારંભિક મૂલ્ય. જો પ્રદાન કરવામાં ન આવે, તો સોર્સ ઇટરેટરમાંથી પ્રથમ મૂલ્ય પ્રારંભિક મૂલ્ય તરીકે ઉપયોગમાં લેવાશે, અને એક્યુમ્યુલેટર ફંક્શન બીજા મૂલ્યથી શરૂ કરીને કૉલ કરવામાં આવશે.
ઉપયોગના કેસો અને ઉદાહરણો
`scan` હેલ્પર અતિ બહુપયોગી છે અને અસિંક્રનસ ડેટા સ્ટ્રીમ્સ સાથે સંકળાયેલા વિવિધ દૃશ્યોમાં તેનો ઉપયોગ કરી શકાય છે. અહીં કેટલાક ઉદાહરણો છે:
૧. ચાલતો સરવાળો ગણવો
કલ્પના કરો કે તમારી પાસે એક અસિંક ઇટરેટર છે જે ટ્રાન્ઝેક્શનની રકમો ઉત્સર્જિત કરે છે. તમે આ ટ્રાન્ઝેક્શન્સનો ચાલતો સરવાળો ગણવા માટે `scan` નો ઉપયોગ કરી શકો છો.
async function* generateTransactions() {
yield 10;
yield 20;
yield 30;
}
async function main() {
const transactions = generateTransactions();
const runningTotals = scan(transactions, (acc, value) => acc + value, 0);
for await (const total of runningTotals) {
console.log(total); // Output: 10, 30, 60
}
}
main();
આ ઉદાહરણમાં, `accumulator` ફંક્શન ફક્ત વર્તમાન ટ્રાન્ઝેક્શનની રકમને પાછલા સરવાળામાં ઉમેરે છે. 0 નું `initialValue` સુનિશ્ચિત કરે છે કે ચાલતો સરવાળો શૂન્યથી શરૂ થાય છે.
૨. ડેટાને એરેમાં સંચિત કરવો
તમે અસિંક ઇટરેટરમાંથી ડેટાને એરેમાં સંચિત કરવા માટે `scan` નો ઉપયોગ કરી શકો છો. આ સમય જતાં ડેટા એકત્રિત કરવા અને તેને બેચમાં પ્રોસેસ કરવા માટે ઉપયોગી થઈ શકે છે.
async function* fetchData() {
yield { id: 1, name: 'Alice' };
yield { id: 2, name: 'Bob' };
yield { id: 3, name: 'Charlie' };
}
async function main() {
const dataStream = fetchData();
const accumulatedData = scan(dataStream, (acc, value) => [...acc, value], []);
for await (const data of accumulatedData) {
console.log(data); // Output: [{id: 1, name: 'Alice'}], [{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}], [{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}, {id: 3, name: 'Charlie'}]
}
}
main();
અહીં, `accumulator` ફંક્શન સ્પ્રેડ ઓપરેટર (`...`) નો ઉપયોગ કરીને પાછલા બધા તત્વો અને વર્તમાન મૂલ્ય ધરાવતો નવો એરે બનાવે છે. `initialValue` એક ખાલી એરે છે.
૩. રેટ લિમિટર લાગુ કરવું
એક વધુ જટિલ ઉપયોગનો કેસ રેટ લિમિટર લાગુ કરવાનો છે. તમે `scan` નો ઉપયોગ કરીને ચોક્કસ સમય વિંડોમાં કરવામાં આવેલી વિનંતીઓની સંખ્યાને ટ્રેક કરી શકો છો અને જો રેટ લિમિટ ઓળંગાઈ જાય તો પછીની વિનંતીઓને વિલંબિત કરી શકો છો.
async function* generateRequests() {
// Simulate incoming requests
yield Date.now();
await new Promise(resolve => setTimeout(resolve, 200));
yield Date.now();
await new Promise(resolve => setTimeout(resolve, 100));
yield Date.now();
}
async function main() {
const requests = generateRequests();
const rateLimitWindow = 1000; // 1 second
const maxRequestsPerWindow = 2;
async function* rateLimitedRequests(source, window, maxRequests) {
let queue = [];
for await (const requestTime of source) {
queue.push(requestTime);
queue = queue.filter(t => requestTime - t < window);
if (queue.length > maxRequests) {
const earliestRequest = queue[0];
const delay = window - (requestTime - earliestRequest);
console.log(`Rate limit exceeded. Delaying for ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
yield requestTime;
}
}
const limited = rateLimitedRequests(requests, rateLimitWindow, maxRequestsPerWindow);
for await (const requestTime of limited) {
console.log(`Request processed at ${requestTime}`);
}
}
main();
આ ઉદાહરણ `scan` નો આંતરિક રીતે (`rateLimitedRequests` ફંક્શનમાં) વિનંતી ટાઇમસ્ટેમ્પ્સની કતાર જાળવવા માટે ઉપયોગ કરે છે. તે તપાસે છે કે રેટ લિમિટ વિંડોમાં વિનંતીઓની સંખ્યા મહત્તમ મંજૂર સંખ્યા કરતાં વધી ગઈ છે કે નહીં. જો તેમ થાય, તો તે જરૂરી વિલંબની ગણતરી કરે છે અને વિનંતી આપતા પહેલા વિરામ લે છે.
૪. રીઅલ-ટાઇમ ડેટા એગ્રીગેટર બનાવવું (વૈશ્વિક ઉદાહરણ)
એક વૈશ્વિક નાણાકીય એપ્લિકેશનનો વિચાર કરો જેને વિવિધ એક્સચેન્જોમાંથી રીઅલ-ટાઇમ સ્ટોક ભાવોને એકત્રિત કરવાની જરૂર છે. એક અસિંક ઇટરેટર ન્યૂયોર્ક સ્ટોક એક્સચેન્જ (NYSE), લંડન સ્ટોક એક્સચેન્જ (LSE), અને ટોક્યો સ્ટોક એક્સચેન્જ (TSE) જેવા એક્સચેન્જોમાંથી ભાવ અપડેટ્સ સ્ટ્રીમ કરી શકે છે. `scan` નો ઉપયોગ તમામ એક્સચેન્જોમાં કોઈ ચોક્કસ સ્ટોક માટે ચાલતી સરેરાશ અથવા ઊંચા/નીચા ભાવને જાળવવા માટે થઈ શકે છે.
// Simulate streaming stock prices from different exchanges
async function* generateStockPrices() {
yield { exchange: 'NYSE', symbol: 'AAPL', price: 170.50 };
yield { exchange: 'LSE', symbol: 'AAPL', price: 170.75 };
await new Promise(resolve => setTimeout(resolve, 50));
yield { exchange: 'TSE', symbol: 'AAPL', price: 170.60 };
}
async function main() {
const stockPrices = generateStockPrices();
// Use scan to calculate a running average price
const runningAverages = scan(
stockPrices,
(acc, priceUpdate) => {
const { total, count } = acc;
return { total: total + priceUpdate.price, count: count + 1 };
},
{ total: 0, count: 0 }
);
for await (const averageData of runningAverages) {
const averagePrice = averageData.total / averageData.count;
console.log(`Running average price: ${averagePrice.toFixed(2)}`);
}
}
main();
આ ઉદાહરણમાં, `accumulator` ફંક્શન ભાવોનો ચાલતો સરવાળો અને પ્રાપ્ત થયેલ અપડેટ્સની સંખ્યાની ગણતરી કરે છે. પછી આ સંચિત મૂલ્યોમાંથી અંતિમ સરેરાશ ભાવની ગણતરી કરવામાં આવે છે. આ વિવિધ વૈશ્વિક બજારોમાં સ્ટોક ભાવનું રીઅલ-ટાઇમ દૃશ્ય પ્રદાન કરે છે.
૫. વૈશ્વિક સ્તરે વેબસાઇટ ટ્રાફિકનું વિશ્લેષણ કરવું
એક વૈશ્વિક વેબ એનાલિટિક્સ પ્લેટફોર્મની કલ્પના કરો જે વિશ્વભરમાં સ્થિત સર્વર્સમાંથી વેબસાઇટ મુલાકાત ડેટાના સ્ટ્રીમ્સ મેળવે છે. દરેક ડેટા પોઇન્ટ વેબસાઇટની મુલાકાત લેતા વપરાશકર્તાનું પ્રતિનિધિત્વ કરે છે. `scan` નો ઉપયોગ કરીને, અમે રીઅલ ટાઇમમાં દેશ દીઠ પેજ વ્યુઝના વલણનું વિશ્લેષણ કરી શકીએ છીએ. ચાલો કહીએ કે ડેટા આના જેવો દેખાય છે: `{ country: "US", page: "homepage", timestamp: 1678886400 }`.
async function* generateWebsiteVisits() {
yield { country: 'US', page: 'homepage', timestamp: Date.now() };
yield { country: 'CA', page: 'product', timestamp: Date.now() };
yield { country: 'UK', page: 'blog', timestamp: Date.now() };
yield { country: 'US', page: 'product', timestamp: Date.now() };
}
async function main() {
const visitStream = generateWebsiteVisits();
const pageViewCounts = scan(
visitStream,
(acc, visit) => {
const { country } = visit;
const newAcc = { ...acc };
newAcc[country] = (newAcc[country] || 0) + 1;
return newAcc;
},
{}
);
for await (const counts of pageViewCounts) {
console.log('Page view counts by country:', counts);
}
}
main();
અહીં, `accumulator` ફંક્શન દરેક દેશ માટે એક કાઉન્ટર અપડેટ કરે છે. આઉટપુટ નવી મુલાકાત ડેટા આવતાની સાથે દરેક દેશ માટે સંચિત પેજ વ્યુ કાઉન્ટ્સ બતાવશે.
`scan` નો ઉપયોગ કરવાના ફાયદા
`scan` હેલ્પર અસિંક્રનસ ડેટા સ્ટ્રીમ્સ સાથે કામ કરતી વખતે ઘણા ફાયદાઓ પ્રદાન કરે છે:
- ઘોષણાત્મક શૈલી: `scan` તમને સંચિત પ્રક્રિયા તર્કને ઘોષણાત્મક અને સંક્ષિપ્ત રીતે વ્યક્ત કરવાની મંજૂરી આપે છે, જે કોડની વાંચનીયતા અને જાળવણીક્ષમતામાં સુધારો કરે છે.
- અસિંક્રનસ હેન્ડલિંગ: તે એક્યુમ્યુલેટર ફંક્શનની અંદર અસિંક્રનસ ઓપરેશન્સને સરળતાથી હેન્ડલ કરે છે, જે તેને I/O-બાઉન્ડ કાર્યો સાથે સંકળાયેલા જટિલ દૃશ્યો માટે યોગ્ય બનાવે છે.
- રીઅલ-ટાઇમ પ્રોસેસિંગ: `scan` ડેટા સ્ટ્રીમ્સનું રીઅલ-ટાઇમ પ્રોસેસિંગ સક્ષમ કરે છે, જે તમને ફેરફારો થતાં જ પ્રતિક્રિયા આપવા દે છે.
- કમ્પોઝિબિલિટી: જટિલ ડેટા પ્રોસેસિંગ પાઇપલાઇન્સ બનાવવા માટે તેને અન્ય અસિંક ઇટરેટર હેલ્પર્સ સાથે સરળતાથી કંપોઝ કરી શકાય છે.
`scan` લાગુ કરવું (જો તે ઉપલબ્ધ ન હોય તો)
જ્યારે કેટલીક લાઇબ્રેરીઓ બિલ્ટ-ઇન `scan` હેલ્પર પ્રદાન કરે છે, ત્યારે જરૂર પડ્યે તમે સરળતાથી તમારું પોતાનું બનાવી શકો છો. અહીં એક સરળ અમલીકરણ છે:
async function* scan(sourceIterator, accumulator, initialValue) {
let accumulatedValue = initialValue;
let first = true;
for await (const value of sourceIterator) {
if (first && initialValue === undefined) {
accumulatedValue = value;
first = false;
} else {
accumulatedValue = accumulator(accumulatedValue, value);
}
yield accumulatedValue;
}
}
આ અમલીકરણ સોર્સ ઇટરેટર પર પુનરાવર્તન કરે છે અને દરેક મૂલ્ય પર એક્યુમ્યુલેટર ફંક્શન લાગુ કરે છે, સંચિત પરિણામ આપે છે. તે એવા કેસને હેન્ડલ કરે છે જ્યાં કોઈ `initialValue` પ્રદાન કરવામાં ન આવે, જેમાં તે સોર્સ ઇટરેટરમાંથી પ્રથમ મૂલ્યને પ્રારંભિક મૂલ્ય તરીકે ઉપયોગ કરે છે.
`reduce` સાથે સરખામણી
`scan` ને `reduce` થી અલગ પાડવું મહત્વપૂર્ણ છે. જ્યારે બંને ઇટરેટર્સ પર કાર્ય કરે છે અને એક્યુમ્યુલેટર ફંક્શનનો ઉપયોગ કરે છે, ત્યારે તેઓ તેમના વર્તન અને આઉટપુટમાં ભિન્ન હોય છે.
- `scan` દરેક પુનરાવર્તન માટે સંચિત મૂલ્ય ઉત્સર્જિત કરે છે, જે સંચયનો ચાલતો ઇતિહાસ પ્રદાન કરે છે.
- `reduce` ઇટરેટરમાંના તમામ તત્વોની પ્રક્રિયા કર્યા પછી ફક્ત અંતિમ સંચિત મૂલ્ય ઉત્સર્જિત કરે છે.
તેથી, `scan` એવા દૃશ્યો માટે યોગ્ય છે જ્યાં તમારે સંચયની મધ્યવર્તી સ્થિતિઓને ટ્રેક કરવાની જરૂર હોય છે, જ્યારે `reduce` ત્યારે યોગ્ય છે જ્યારે તમને ફક્ત અંતિમ પરિણામની જરૂર હોય.
ભૂલ સંચાલન (Error Handling)
અસિંક્રનસ ઇટરેટર્સ અને `scan` સાથે કામ કરતી વખતે, ભૂલોને યોગ્ય રીતે સંભાળવી ખૂબ જ મહત્વપૂર્ણ છે. પુનરાવર્તન પ્રક્રિયા દરમિયાન અથવા એક્યુમ્યુલેટર ફંક્શનની અંદર ભૂલો થઈ શકે છે. તમે આ ભૂલોને પકડવા અને સંભાળવા માટે `try...catch` બ્લોક્સનો ઉપયોગ કરી શકો છો.
async function* generatePotentiallyFailingData() {
yield 1;
yield 2;
throw new Error('Something went wrong!');
yield 3;
}
async function main() {
const dataStream = generatePotentiallyFailingData();
try {
const accumulatedData = scan(dataStream, (acc, value) => acc + value, 0);
for await (const data of accumulatedData) {
console.log(data);
}
} catch (error) {
console.error('An error occurred:', error);
}
}
main();
આ ઉદાહરણમાં, `try...catch` બ્લોક `generatePotentiallyFailingData` ઇટરેટર દ્વારા ફેંકવામાં આવેલી ભૂલને પકડે છે. પછી તમે ભૂલને યોગ્ય રીતે સંભાળી શકો છો, જેમ કે તેને લોગ કરવું અથવા ઓપરેશનનો ફરી પ્રયાસ કરવો.
નિષ્કર્ષ
`scan` હેલ્પર જાવાસ્ક્રિપ્ટ અસિંક ઇટરેટર્સ પર અસિંક્રનસ સંચિત પ્રક્રિયા કરવા માટે એક શક્તિશાળી સાધન છે. તે તમને જટિલ ડેટા રૂપાંતરણોને ઘોષણાત્મક અને સંક્ષિપ્ત રીતે વ્યક્ત કરવા, અસિંક્રનસ ઓપરેશન્સને યોગ્ય રીતે સંભાળવા અને ડેટા સ્ટ્રીમ્સને રીઅલ-ટાઇમમાં પ્રોસેસ કરવા સક્ષમ બનાવે છે. તેની કાર્યક્ષમતા અને ઉપયોગના કેસોને સમજીને, તમે વધુ મજબૂત અને કાર્યક્ષમ અસિંક્રનસ એપ્લિકેશન્સ બનાવવા માટે `scan` નો લાભ લઈ શકો છો. ભલે તમે ચાલતા સરવાળાની ગણતરી કરી રહ્યાં હોવ, ડેટાને એરેમાં સંચિત કરી રહ્યાં હોવ, રેટ લિમિટર્સ લાગુ કરી રહ્યાં હોવ, અથવા રીઅલ-ટાઇમ ડેટા એગ્રીગેટર્સ બનાવી રહ્યાં હોવ, `scan` તમારા કોડને સરળ બનાવી શકે છે અને તેના એકંદર પ્રદર્શનમાં સુધારો કરી શકે છે. ભૂલ સંચાલનનો વિચાર કરવાનું યાદ રાખો અને જ્યારે તમને તમારા અસિંક્રનસ ડેટા સ્ટ્રીમ્સની પ્રક્રિયા દરમિયાન મધ્યવર્તી સંચિત મૂલ્યોની ઍક્સેસની જરૂર હોય ત્યારે `reduce` પર `scan` પસંદ કરો. RxJS જેવી લાઇબ્રેરીઓનું અન્વેષણ કરવાથી પ્રતિક્રિયાશીલ પ્રોગ્રામિંગ પેરાડાઇમ્સમાં `scan` ની તમારી સમજ અને વ્યવહારુ એપ્લિકેશનને વધુ વધારી શકે છે.